package com.zxj.system.service.sysuser.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.zxj.common.core.contains.Constants;
import com.zxj.common.core.contains.HttpConstants;
import com.zxj.common.core.domain.R;
import com.zxj.common.core.domain.cache.LoginUserCache;
import com.zxj.common.core.enums.ResultCode;
import com.zxj.common.core.enums.UserIdentity;
import com.zxj.common.security.exception.ServiceException;
import com.zxj.common.security.service.TokenService;
import com.zxj.system.domain.sysuser.dto.LoginDTO;
import com.zxj.system.domain.sysuser.dto.SysUserAddDTO;
import com.zxj.system.domain.sysuser.entity.SysUser;
import com.zxj.system.domain.sysuser.vo.LoginUserVO;
import com.zxj.system.domain.sysuser.vo.LoginVO;
import com.zxj.system.mapper.sysuser.SysUserMapper;
import com.zxj.system.service.sysuser.ISysUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zxj.system.utils.BCryptUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 系统管理员用户表 服务实现类
 * </p>
 *
 * @author zxj2022
 * @since 2024-12-19
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    private final TokenService tokenService;

    @Value("${oj.jwt.token.secret}")
    private String secret; //  读取配置文件中的 jwt token 密钥

    /**
     * 登录
     *
     * @param loginDTO 登录请求信息
     * @return 返回的信息
     */
    @Override
    public LoginVO login(LoginDTO loginDTO) {
        // 1. 根据账号查询用户
        SysUser sysUser = this.lambdaQuery()
                .eq(SysUser::getUserAccount, loginDTO.getUserAccount()).one();

        // 2. 判断用户是否存在
        if (sysUser == null) {
            log.error("用户不存在，userAccount:{}", loginDTO.getUserAccount());
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }

        // 3. 判断密码是否正确
        if (!BCryptUtils.matchesPassword(loginDTO.getPassword(), sysUser.getPassword())) {
            log.error("密码错误，userAccount:{}", loginDTO.getUserAccount());
            throw new ServiceException(ResultCode.FAILED_LOGIN);
        }

        // 4. 账号密码正确， 生成 jwt token
        String token = tokenService.createToken(
                sysUser.getUserId(), // 用户ID - 用于后续查询用户信息的
                secret, // 密钥
                UserIdentity.ADMIN.getValue(), // 用户身份
                sysUser.getNickName(), // 用户昵称
                null); // 用户头像URL, 管理员无头像

        if (StrUtil.isBlank(token)) {
            log.error("生成 token 失败，userAccount:{}", loginDTO.getUserAccount());
            throw new ServiceException(ResultCode.ERROR);
        }

        log.debug("登录成功，userAccount:{}, token: {}", loginDTO.getUserAccount(), token);

        // 5. 构建返回信息
        LoginVO loginVO = new LoginVO();
        loginVO.setToken(token);

        return loginVO;
    }

    /**
     * 新增管理员
     * @param sysUserAddDTO
     * @return
     */
    @Override
    public boolean add(SysUserAddDTO sysUserAddDTO) {
        // 1. 判断用户是否存在
        SysUser sysUser = this.lambdaQuery()
                .eq(SysUser::getUserAccount, sysUserAddDTO.getUserAccount()).one();
        if (sysUser != null) {
            log.error("用户已存在，userAccount:{}", sysUserAddDTO.getUserAccount());
            throw new ServiceException(ResultCode.AILED_USER_EXISTS);
        }

        // 2. 新增用户
        // 2.1 将DTO转为实体
        SysUser addSysUser = BeanUtil.copyProperties(sysUserAddDTO, SysUser.class);
        // 2.2 密码加密
        addSysUser.setPassword(BCryptUtils.encryptPassword(sysUserAddDTO.getPassword()));
        // 2.3 设置创建者-规定为系统用户
        addSysUser.setCreateBy(Constants.SYSTEM_USER_ID);

        // 3. 保存用户
        return this.save(addSysUser);
    }

    /**
     * 依据 token 从 redis 中获取用户信息
     * @param token 前端请求格式: "Bearer ${token}", 所以要去掉前缀
     * @return
     */
    @Override
    public R<LoginUserVO> info(String token) {
        // 1. 如果前端设置了令牌前缀，则裁剪掉前缀
        token = removeTokenPrefix(token);
        // 2. 调用 token 服务从 redis 中获取用户信息
        LoginUserCache loginUserCache =  tokenService.getLoginUserCacheFromToken(token, secret);

        // 3. 封装返回信息
        if (loginUserCache == null) {
            // 信息查询不到, 抛登录失效异常
            log.error("用户信息查询不到，token:{}", token);
            throw new ServiceException(ResultCode.FAILED_UNAUTHORIZED);
        }
        return R.ok(BeanUtil.copyProperties(loginUserCache, LoginUserVO.class));
    }

    /**
     * 退出登录
     * @param token jwt token
     * @return true: 退出成功, false: 退出失败
     */
    @Override
    public boolean logout(String token) {
        // 1. 如果前端设置了令牌前缀，则裁剪掉前缀
        token = removeTokenPrefix(token);
        // 2. 调用 token 服务删除登录用户信息
        return tokenService.deleteLoginUser(token, secret);
    }

    private static String removeTokenPrefix(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return token;
    }
}
